package org.jflame.context.filemanager;

import org.jflame.commons.codec.Transcoder;
import org.jflame.commons.file.FileHelper;
import org.jflame.commons.model.Chars;
import org.jflame.commons.net.HttpHelper;
import org.jflame.commons.util.CollectionHelper;
import org.jflame.commons.util.DateHelper;
import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.MapHelper;
import org.jflame.commons.util.StringHelper;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

import com.qcloud.cos.COS;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.auth.COSSigner;
import com.qcloud.cos.auth.COSSignerConstants;
import com.qcloud.cos.endpoint.UserSpecifiedEndpointBuilder;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.BucketCrossOriginConfiguration;
import com.qcloud.cos.model.BucketRefererConfiguration;
import com.qcloud.cos.model.CORSRule;
import com.qcloud.cos.model.CORSRule.AllowedMethods;
import com.qcloud.cos.model.COSObjectSummary;
import com.qcloud.cos.model.CopyObjectRequest;
import com.qcloud.cos.model.CopyResult;
import com.qcloud.cos.model.DeleteObjectsRequest;
import com.qcloud.cos.model.DeleteObjectsRequest.KeyVersion;
import com.qcloud.cos.model.DeleteObjectsResult;
import com.qcloud.cos.model.DeleteObjectsResult.DeletedObject;
import com.qcloud.cos.model.GetObjectRequest;
import com.qcloud.cos.model.ListObjectsRequest;
import com.qcloud.cos.model.ObjectListing;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.SetBucketCrossOriginConfigurationRequest;
import com.qcloud.cos.model.SetBucketRefererConfigurationRequest;
import com.qcloud.cos.model.UploadResult;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.Copy;
import com.qcloud.cos.transfer.Download;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;

/**
 * 腾讯cos sdk文件管理实现
 * 
 * @author charles.zhang
 */
public class TencentCosFileManager extends OssFileManager<OssManagerCfg> {

    private final static String rootDomain = "myqcloud.com";
    private TransferManager transferManager;
    // private String bucketEndpoint;// 当前bucket的访问地址
    // private ClientConfig cosClientConfig;

    public TencentCosFileManager(OssManagerCfg cfg) {
        super(cfg);
        init();
    }

    public TencentCosFileManager(String serverUrl, final String accessId, final String accessSecret, String region,
            String bucketName) {
        this(new OssManagerCfg(serverUrl, accessId, accessSecret, region, bucketName));
    }

    private void init() {
        ClientConfig cosClientConfig = new ClientConfig();
        OssManagerCfg managerCfg = getConfig();
        if (managerCfg.isEnableHttps()) {
            cosClientConfig.setHttpProtocol(HttpProtocol.https);
        } else {
            cosClientConfig.setHttpProtocol(HttpProtocol.http);
        }
        // set http proxy
        if (StringHelper.isNotEmpty(managerCfg.getProxyIp())) {
            cosClientConfig.setHttpProxyIp(managerCfg.getProxyIp());
        }
        if (managerCfg.getProxyPort() > 0) {
            cosClientConfig.setHttpProxyPort(managerCfg.getProxyPort());
        }
        if (StringHelper.isNotEmpty(managerCfg.getProxyUser())) {
            cosClientConfig.setProxyUsername(managerCfg.getProxyUser());
        }
        if (StringHelper.isNotEmpty(managerCfg.getProxyPassword())) {
            cosClientConfig.setProxyPassword(managerCfg.getProxyPassword());
        }

        // 没有自定义域名,用默认域名必须有region
        if (StringHelper.isEmpty(managerCfg.getServer())) {
            if (StringHelper.isEmpty(managerCfg.getRegion())) {
                throw new RuntimeException("cos must configure region if no server is configured");
            }
            cosClientConfig.setRegion(new Region(managerCfg.getRegion()));
        } else {
            // 使用bucket自定义域名
            if (managerCfg.getServer()
                    .indexOf(rootDomain) < 1) {
                managerCfg.setCname(true);
                UserSpecifiedEndpointBuilder endpointBuilder = new UserSpecifiedEndpointBuilder(managerCfg.getServer(),
                        "service.cos.myqcloud.com");

                cosClientConfig.setEndpointBuilder(endpointBuilder);
            }
            if (StringHelper.isNotEmpty(managerCfg.getRegion())) {
                cosClientConfig.setRegion(new Region(managerCfg.getRegion()));
            }
        }

        if (managerCfg.getConnectionTimeout() > 0) {
            cosClientConfig.setConnectionTimeout(managerCfg.connectionTimeoutInMillis());
        }
        if (managerCfg.getReadTimeout() > 0) {
            cosClientConfig.setSocketTimeout(managerCfg.readTimeoutInMillis());
        }

        COSCredentials cred = new BasicCOSCredentials(managerCfg.getAccessId(), managerCfg.getAccessSecret());
        COSClient cosClient = new COSClient(cred, cosClientConfig);

        transferManager = new TransferManager(cosClient);
        // 分块上传阈值和分块大小分别为 5MB 和 1MB
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
        transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
        transferManager.setConfiguration(transferManagerConfiguration);

        if (!managerCfg.isCname()) {
            String bucketEndpoint = cosClientConfig.getEndpointBuilder()
                    .buildGeneralApiEndpoint(managerCfg.getBucket());
            managerCfg.setServer(cosClientConfig.getHttpProtocol()
                    .name() + "://" + bucketEndpoint);
        }
        // setBucketEndpoint();
    }

    @Override
    public String save(File file, String saveDir, Map<String,String> fileMeta) throws FileAccessException {
        String newName = createNewFileId(saveDir, file.getName());
        PutObjectRequest putObjectRequest = new PutObjectRequest(getCurrentBucket(), newName, file);

        ObjectMetadata objectMetadata = new ObjectMetadata();
        Optional<String> mimeType = guessContentType(newName);
        if (mimeType.isPresent()) {
            objectMetadata.setContentType(mimeType.get());
        }
        if (MapHelper.isNotEmpty(fileMeta)) {
            objectMetadata.setUserMetadata(fileMeta);
            putObjectRequest.withMetadata(objectMetadata);
        }

        try {
            // 高级接口会返回一个异步结果Upload
            // 可同步地调用 waitForUploadResult 方法等待上传完成，成功返回 UploadResult, 失败抛出异常
            Upload upload = transferManager.upload(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();
            return uploadResult.getKey();
        } catch (CosClientException | InterruptedException e) {
            throw new FileAccessException("上传文件到cos失败,文件:" + file.getName(), e);
        }
    }

    @Override
    public String save(InputStream fileStream, String filePath, Map<String,String> fileMeta, boolean replaceExisting)
            throws FileAccessException {
        String fileId;
        if (replaceExisting) {
            fileId = filePath;
        } else {
            fileId = createNewFileId(StringHelper.substringBeforeLast(filePath, "/"), FileHelper.getFilename(filePath));
        }

        PutObjectRequest putObjectRequest = null;
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (MapHelper.isNotEmpty(fileMeta)) {
            objectMetadata.setUserMetadata(fileMeta);
        }
        Optional<String> mimeType = guessContentType(fileId);
        if (mimeType.isPresent()) {
            objectMetadata.setContentType(mimeType.get());
        }
        try {
            if (objectMetadata.getContentLength() == 0) {
                if (fileStream instanceof ByteArrayInputStream || fileStream instanceof FileInputStream) {
                    objectMetadata.setContentLength(fileStream.available());
                }
                if (fileStream instanceof ByteArrayInputStream || fileStream instanceof FileInputStream
                        || fileStream instanceof BufferedInputStream) {
                    int objsize;
                    try {
                        objsize = fileStream.available();
                        if (objsize > 0) {
                            objectMetadata.setContentLength(objsize);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            putObjectRequest = new PutObjectRequest(getCurrentBucket(), fileId, fileStream, objectMetadata);
            Upload upload = transferManager.upload(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();
            return uploadResult.getKey();
        } catch (CosClientException | InterruptedException | IOException e) {
            throw new FileAccessException("上传文件到cos失败,文件:" + filePath, e);
        } finally {
            IOHelper.closeQuietly(fileStream);
        }
    }

    @Override
    public byte[] readBytes(String filePath) throws FileAccessException {
        File f = read(filePath);
        try {
            return Files.readAllBytes(f.toPath());
        } catch (IOException e) {
            throw new FileAccessException("读取文件异常" + filePath, e);
        }
    }

    @Override
    public File read(String filePath) throws FileAccessException {
        String bucket = getCurrentBucket();
        String key = getFileId(filePath);
        Path tmpPath = mappingTmpFile(key);
        // 使用缓存的临时文件
        Optional<File> localCacheFileOpl = getTmpCacheFile(tmpPath);
        if (localCacheFileOpl.isPresent()) {
            return localCacheFileOpl.get();
        }
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
        File tmpFile = tmpPath.toFile();
        try {
            Download download = transferManager.download(getObjectRequest, tmpFile);
            download.waitForCompletion();
            return tmpFile;
        } catch (CosServiceException e) {
            if ("NoSuchKey".equals(e.getErrorCode())) {
                throw new FileAccessException("读取文件异常" + filePath, FileAccessException.FILE_NOT_FOUND, e);
            } else {
                throw new FileAccessException("读取文件异常" + filePath, e);
            }
        } catch (CosClientException | InterruptedException e) {
            throw new FileAccessException("读取文件异常" + filePath, e);
        }
    }

    @Override
    public InputStream readStream(String filePath) throws FileAccessException {
        File f = read(filePath);
        try {
            return new FileInputStream(f);
        } catch (FileNotFoundException e) {
            throw new FileAccessException(e.getMessage(), FileAccessException.FILE_NOT_FOUND);
        }
    }

    @Override
    public void download(String filePath, Path savePath) throws FileAccessException {
        String bucket = getCurrentBucket();
        String key = getFileId(filePath);
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
        try {
            Download download = transferManager.download(getObjectRequest, savePath.toFile());
            download.waitForCompletion();
        } catch (CosServiceException e) {
            if ("NoSuchKey".equals(e.getErrorCode())) {
                throw new FileAccessException("下载文件异常" + filePath, FileAccessException.FILE_NOT_FOUND, e);
            } else {
                throw new FileAccessException("下载文件异常" + filePath, e);
            }
        } catch (CosClientException | InterruptedException e) {
            throw new FileAccessException("cos读取文件异常" + filePath, e);
        }

    }

    @Override
    public int delete(String... filePaths) throws FileAccessException {
        return delete(getCurrentBucket(), filePaths);
    }

    @Override
    public int delete(String bucketName, String[] filePaths) throws FileAccessException {
        if (ArrayUtils.isEmpty(filePaths)) {
            throw new IllegalArgumentException();
        }
        List<String> fileKeys = Arrays.asList(filePaths);
        if (logger.isInfoEnabled()) {
            logger.info("删除文件:{}", ArrayUtils.toString(fileKeys));
        }

        List<KeyVersion> keyList = new ArrayList<>();
        for (String key : fileKeys) {
            keyList.add(new KeyVersion(getFileId(key)));
        }

        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName);
        deleteObjectsRequest.setKeys(keyList);

        try {
            DeleteObjectsResult deleteObjectsResult = transferManager.getCOSClient()
                    .deleteObjects(deleteObjectsRequest);
            List<DeletedObject> deleteObjectResultArray = deleteObjectsResult.getDeletedObjects();
            return deleteObjectResultArray.size();
        } catch (MultiObjectDeleteException mde) {
            // 如果部分删除成功部分失败, 返回 MultiObjectDeleteException
            List<DeletedObject> deleteObjects = mde.getDeletedObjects();
            // List<DeleteError> deleteErrors = mde.getErrors();
            logger.error("部分文件删除失败:{}", mde.getErrorMessage());
            return deleteObjects.size();
        } catch (CosClientException e) {
            throw new FileAccessException("删除cos文件失败", e);
        }
    }

    @Override
    public SignedUrl generatePresignedPutUrl(PresignedUrlArgs args) {
        return generatePresignedUrl(HttpMethodName.PUT, args);
    }

    @Override
    public String generatePresignedGetUrl(PresignedUrlArgs args) {
        return generatePresignedUrl(HttpMethodName.GET, args).urlStr();
    }

    private SignedUrl generatePresignedUrl(HttpMethodName method, PresignedUrlArgs args) {
        // https://cloud.tencent.com/document/product/436/35217
        Date expiration = DateUtils.addSeconds(new Date(), args.getExpireTime());
        String fileId;

        Map<String,String> respMap = new LinkedHashMap<>();
        if (method == HttpMethodName.GET || method == HttpMethodName.DELETE) {
            fileId = getFileId(args.getFileNameOrExt());
        } else {
            fileId = createNewFileId(args.getSavePath(), args.getFileNameOrExt());
            Map<String,String> headers = setContentType(fileId, args.getHeaders());
            if (MapHelper.isNotEmpty(headers)) {
                respMap.putAll(headers);
            }

            // 添加用户自定义元信息。
            /*if (MapHelper.isNotEmpty(args.getUserMetadata())) {
                respMap.putAll(args.getUserMetadata());
            }*/
        }

        URL surl = transferManager.getCOSClient()
                .generatePresignedUrl(getCurrentBucket(), fileId, expiration, method,
                        ObjectUtils.defaultIfNull(args.getHeaders(), new HashMap<>()),
                        ObjectUtils.defaultIfNull(args.getQueryParams(), new HashMap<>()));
        return new SignedUrl(surl, respMap);
    }

    public Map<String,String> generatePostObjectParams(PresignedUrlArgs args) {
        String fileId = createNewFileId(args.getSavePath(), args.getFileNameOrExt());
        Optional<String> mimeType = guessContentType(fileId);

        long startTimestamp = DateHelper.unixTimestamp();
        long endTimestamp = startTimestamp + args.getExpireTime();
        String endTimestampStr = DateFormatUtils.format(endTimestamp * DateHelper.MILLIS_PER_SECOND,
                "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        String keyTime = startTimestamp + ";" + endTimestamp;
        // 上传限定条件
        /*{
            "expiration": "2019-08-30T09:38:12.414Z",
            "conditions": [
                { "acl": "default" },
                { "bucket": "examplebucket-1250000000" },
                [ "starts-with", "$key", "folder/subfolder/" ],
                [ "starts-with", "$Content-Type", "image/" ],
                [ "starts-with", "$success_action_redirect", "https://my.website/" ],
                [ "eq", "$x-cos-server-side-encryption", "AES256" ],
                [ "content-length-range", minNum, maxNum ]
                { "q-sign-algorithm": "sha1" },
                { "q-ak": "AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q" },
                { "q-sign-time": "1567150692;1567157892" }
            ]
        }*/

        Map<String,String> respMap = new LinkedHashMap<String,String>();
        String objFmt = "{\"%s\":\"%s\"}";
        OssManagerCfg clientCfg = getConfig();

        StringJoiner condiJoiner = new StringJoiner(",", "[", "]")
                .add(String.format(objFmt, "bucket", clientCfg.getBucket()))
                .add(String.format(objFmt, COSSignerConstants.Q_AK, clientCfg.getAccessId()))
                .add(String.format(objFmt, COSSignerConstants.Q_SIGN_ALGORITHM_KEY,
                        COSSignerConstants.Q_SIGN_ALGORITHM_VALUE))
                .add(String.format(objFmt, COSSignerConstants.Q_SIGN_TIME, keyTime));
        if (args.getMaxFileSize() > 0) {
            condiJoiner.add(String.format("[\"content-length-range\",%d,%d]", 0, args.getMaxFileSize()));
        }
        if (mimeType.isPresent()) {
            condiJoiner.add("[\"starts-with\",\"$Content-Type\",\"" + mimeType.get() + "\"]");
        }

        StringJoiner policyJoiner = new StringJoiner(",", "{", "}")
                .add(String.format("\"expiration\":\"%s\"", endTimestampStr))
                .add(String.format("\"conditions\":%s", condiJoiner.toString()));
        if (logger.isDebugEnabled()) {
            logger.debug("cos policy:{}", policyJoiner.toString());
        }
        String policy = policyJoiner.toString();
        String encodedPolicy = Transcoder.encodeBase64String(policy);
        ClientConfig cosClientConfig = transferManager.getCOSClient()
                .getClientConfig();
        COSSigner cosSigner = cosClientConfig.getCosSigner();
        String signature = cosSigner.buildPostObjectSignature(clientCfg.getAccessSecret(), keyTime, policy);

        respMap.put(COSSignerConstants.Q_AK, clientCfg.getAccessId());
        respMap.put(COSSignerConstants.Q_SIGN_ALGORITHM_KEY, COSSignerConstants.Q_SIGN_ALGORITHM_VALUE);
        respMap.put(COSSignerConstants.Q_KEY_TIME, keyTime);
        respMap.put(COSSignerConstants.Q_SIGNATURE, signature);
        respMap.put("key", fileId);
        if (mimeType.isPresent()) {
            respMap.put(HttpHelper.HEADER_CONTENTTYPE, mimeType.get());
        }
        respMap.put("policy", encodedPolicy);
        respMap.put("host", cosClientConfig.getHttpProtocol()
                .name() + "://"
                + cosClientConfig.getEndpointBuilder()
                        .buildGeneralApiEndpoint(clientCfg.getBucket()));

        return respMap;
    }

    @SuppressWarnings("unused")
    @Override
    public String copy(String srcFilePath, String destFilePath) throws FileAccessException {
        if (Objects.equals(srcFilePath, destFilePath)) {
            throw new IllegalArgumentException("源文件与目标文件路径相同");
        }
        String srcBucket = getCurrentBucket();
        String srckey = getFileId(srcFilePath);

        String destFile;
        String destBucket;
        CopyObjectRequest copyObjectRequest = null;

        if (StringHelper.isEmpty(destFilePath)) {
            int extBefore = srckey.lastIndexOf('.');
            if (extBefore < 1) {
                extBefore = srckey.length();
            }

            destFile = StringHelper.insertAt(srckey, String.format(copy_name_fmt, DateHelper.formatNow("yyMMddHHmm"),
                    RandomStringUtils.random(3, true, true)), extBefore);
            destBucket = srcBucket;
        } else {
            // 复制到别的bucket,目标路径以bucket:开头
            if (destFilePath.startsWith("bucket:")) {
                destBucket = StringHelper.substringBetween(destFilePath, "bucket:", "/");
                destFilePath = StringHelper.substringAfter(destFilePath, "/");
            } else {
                destBucket = srcBucket;
            }
            // String destkey = getFileId(destFilePath);
            // 复制到目录，文件名不变
            if (StringHelper.endChar(destFilePath) == Chars.SLASH) {
                destFile = destFilePath + FileHelper.getFilename(srckey);
            } else {
                destFile = destFilePath;
            }

        }
        copyObjectRequest = new CopyObjectRequest(srcBucket, srckey, destBucket, destFile);
        try {
            Copy copyResult = transferManager.copy(copyObjectRequest);
            CopyResult cr = copyResult.waitForCopyResult();
        } catch (CosClientException | InterruptedException e) {
            throw new FileAccessException("cos复制文件异常:" + srcFilePath, e);
        }
        return destFile;
    }

    @Override
    public List<String> listFiles(String dir, int maxFiles, boolean isExcludeDir) throws FileAccessException {
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(getCurrentBucket());
        if (maxFiles <= 0) {
            maxFiles = 1000;// 最大数
        }
        listObjectsRequest.setMaxKeys(maxFiles);
        int dirLen = dir != null ? dir.length() : 0;
        if (dirLen > 0 && !"/".equals(dir)) {
            if (StringHelper.endChar(dir) != Chars.SLASH) {
                dir = dir + Chars.SLASH;
                dirLen += 1;
            }
            listObjectsRequest.setPrefix(dir);
        }
        List<String> filenames = new ArrayList<>(maxFiles);
        ObjectListing listing;
        try {
            listing = transferManager.getCOSClient()
                    .listObjects(listObjectsRequest);
        } catch (CosClientException e) {
            throw new FileAccessException("cos列举目录文件异常:" + dir, e);
        }

        for (COSObjectSummary objSummary : listing.getObjectSummaries()) {
            filterFilename(objSummary.getKey(), dirLen, isExcludeDir, filenames);
        }
        return filenames;
    }

    @Override
    public void createBucket(String bucketName) {
        transferManager.getCOSClient()
                .createBucket(bucketName);
    }

    /**
     * 设置bucket的请求防盗链http referer
     * 
     * @param allowEmptyReferer 是否允许空的http referer
     * @param refererList 允许的http referer列表
     * @param append true=追加,false=替换更新
     */
    public void setBucketReferer(boolean allowEmptyReferer, List<String> refererList, boolean append) {
        String bucketName = getCurrentBucket();
        BucketRefererConfiguration referer = null;
        if (append) {
            referer = transferManager.getCOSClient()
                    .getBucketRefererConfiguration(bucketName);
            if (refererList != null) {
                referer.getDomainList()
                        .addAll(refererList);
            }
            referer.setRefererType(BucketRefererConfiguration.WHITELIST);
            referer.setEmptyReferConfiguration(
                    allowEmptyReferer ? BucketRefererConfiguration.ALLOW : BucketRefererConfiguration.DENY);

        } else {
            referer = new BucketRefererConfiguration();
            if (refererList != null) {
                referer.setDomainList(refererList);
            }
            referer.setStatus(BucketRefererConfiguration.ENABLED);
            referer.setRefererType(BucketRefererConfiguration.WHITELIST);
            referer.setEmptyReferConfiguration(
                    allowEmptyReferer ? BucketRefererConfiguration.ALLOW : BucketRefererConfiguration.DENY);
        }
        SetBucketRefererConfigurationRequest request = new SetBucketRefererConfigurationRequest(bucketName, referer);

        transferManager.getCOSClient()
                .setBucketRefererConfiguration(request);
    }

    /**
     * 设置跨域规则
     * 
     * @param allowedOrigins
     * @param allowedMethods
     * @param allowedHeaders
     * @param exposeHeaders
     * @param maxAgeSeconds
     */
    public void SetBucketCORS(List<String> allowedOrigins, List<String> allowedMethods, List<String> allowedHeaders,
            List<String> exposeHeaders, Integer maxAgeSeconds) {
        BucketCrossOriginConfiguration corsConfiguration = new BucketCrossOriginConfiguration();
        CORSRule rule = new CORSRule();
        if (CollectionHelper.isNotEmpty(allowedHeaders)) {
            rule.setAllowedHeaders(allowedHeaders);
        }
        if (CollectionHelper.isNotEmpty(exposeHeaders)) {
            rule.setExposedHeaders(exposeHeaders);
        }
        if (CollectionHelper.isNotEmpty(allowedOrigins)) {
            rule.setAllowedOrigins(allowedOrigins);
        }
        if (CollectionHelper.isNotEmpty(allowedMethods)) {
            List<AllowedMethods> methods = new ArrayList<>();
            for (String method : allowedMethods) {
                methods.add(AllowedMethods.fromValue(method));
            }
            rule.setAllowedMethods(methods);
        }
        if (maxAgeSeconds != null) {
            rule.setMaxAgeSeconds(maxAgeSeconds);
        }
        corsConfiguration.withRules(rule);
        SetBucketCrossOriginConfigurationRequest request = new SetBucketCrossOriginConfigurationRequest(
                getCurrentBucket(), corsConfiguration);
        transferManager.getCOSClient()
                .setBucketCrossOriginConfiguration(request);
    }

    @Override
    public void close() throws IOException {
        if (transferManager != null) {
            transferManager.shutdownNow(true);
        }
    }

    /**
     * 返回内部的cosclient
     * 
     * @return
     */
    public COS getClient() {
        return transferManager.getCOSClient();
    }
    /* @Override
    public String toRequestUrl(String filePath) {
        return UrlHelper.mergeUrl(bucketEndpoint, filePath);
    }*/

    /* @Override
    public void switchBucket(String newBucket) {
        // 自定义域名是直接对bucket级的,不能切换bucket
        if (getConfig().isCname()) {
            return;
        }
        super.switchBucket(newBucket);
        setBucketEndpoint();
    }*/

    /* private void setBucketEndpoint() {
        OssManagerCfg cfg = getConfig();
        if (cfg.isCname()) {
            bucketEndpoint = cfg.getServer();
        } else {
            if (StringHelper.isNotEmpty(cfg.getBucket())) {
                bucketEndpoint = transferManager.getCOSClient()
                        .getClientConfig()
                        .getEndpointBuilder()
                        .buildGeneralApiEndpoint(cfg.getBucket());
            }
        }
    }*/

    /*private String extractBucketName(String fileKey) {
        String bucket = getCurrentBucket();
        if (!getConfig().isCname() && isProtocolStarter(fileKey) && fileKey.indexOf(rootDomain) > 1) {
            URI uri = URI.create(fileKey);
            String domain = uri.getHost();
            // bucket地址: <BucketName-APPID>.cos.<Region>.myqcloud.com
            String[] domainLevels = domain.split(".");
            // cos.<region>.myqcloud.com/<BucketName-APPID>/
            if (domainLevels.length == 4) {
                bucket = StringHelper.substringBefore(domain, "/");
            } else {
                if (domainLevels.length >= 5) {
                    return domainLevels[0];
                }
            }
        }
        return bucket;
    }*/

}
